﻿using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace ARchGL.Platform.IO
{
    class ZipHelper2
    {
        #region 压缩文件

        /// <inheritdoc/>
        public void Compress(string fileOrDir, string targetFileName, string password = null, bool overwrite = true, int level = 9)
        {
            List<string> list = new List<string> { fileOrDir };
            Compress(list, targetFileName, password, overwrite, level);
        }

        /// <inheritdoc/>
        public void Compress(List<string> list, string targetFileName, string password = null, bool overwrite = true, int level = 9)
        {
            CheckForCompress(list, targetFileName, overwrite);

            //如果已经存在目标文件，删除
            if (File.Exists(targetFileName))
            {
                File.Delete(targetFileName);
            }

            ZipOutputStream zips = null;
            FileStream fileStream = null;
            try
            {
                fileStream = File.Create(targetFileName);
                zips = new ZipOutputStream(fileStream);
                zips.SetLevel(level % 10);      //压缩等级
                zips.Password = password;
                foreach (string dir in list)
                {
                    if (File.Exists(dir))
                    {
                        AddFile("", dir, zips);
                    }
                    else
                    {
                        CompressFolder("", dir, zips);
                    }
                }
                zips.Finish();
            }
            catch { throw; }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                    fileStream.Dispose();
                }
                if (zips != null)
                {
                    zips.Close();
                    zips.Dispose();
                }
            }
        }

        #region private
        private void CheckForCompress(List<string> files, string targetFileName, bool overwrite)
        {
            //因为files可能来自不同的文件夹，所以不方便自动提供一个默认文件夹，需要提供
            if (!overwrite && File.Exists(targetFileName))
            {
                throw new Exception("目标zip文件已存在！");
            }

            //待压入的文件或文件夹需要真实存在
            foreach (var item in files)
            {
                if (!File.Exists(item) && !Directory.Exists(item))
                {
                    throw new Exception($"文件/文件夹【{item}】不存在！");
                }
            }

            //不能有同名的文件/文件夹
            Dictionary<string, string> dic = new Dictionary<string, string>();
            foreach (var item in files)
            {
                string item_ = item.TrimEnd('/', '\\');
                string fileName = Path.GetFileName(item_);
                if (dic.ContainsKey(fileName))
                {
                    throw new Exception($"选中的文件/文件夹中存在同名冲突：【{dic[fileName]}】，【{item_}】");
                }
                else
                {
                    dic[fileName] = item_;
                }
            }
        }

        private void AddFile(string orignalDir, string file, ZipOutputStream zips)
        {
            //文件
            FileStream StreamToZip = null;
            try
            {
                //加入ZIP文件条目（为压缩文件流提供一个容器）
                StreamToZip = new FileStream(file, FileMode.Open, FileAccess.Read);
                string fileName = Path.GetFileName(file);
                if (!string.IsNullOrEmpty(orignalDir))
                {
                    fileName = orignalDir + Path.DirectorySeparatorChar + fileName;
                }
                ZipEntry z = new ZipEntry(fileName);
                zips.PutNextEntry(z);

                //写入文件流
                int pack = 10240; //10Kb
                byte[] buffer = new byte[pack];

                int size = StreamToZip.Read(buffer, 0, buffer.Length);
                while (size > 0)
                {
                    zips.Write(buffer, 0, size);
                    size = StreamToZip.Read(buffer, 0, buffer.Length);
                }
            }
            catch { throw; }
            finally
            {
                if (StreamToZip != null)
                {
                    StreamToZip.Close();
                    StreamToZip.Dispose();
                }
            }
        }
        private void AddFolder(string orignalDir, string folder, ZipOutputStream zips)
        {
            //文件夹
            folder = folder.TrimEnd('/', '\\');
            string fileName = Path.GetFileName(folder);
            if (!string.IsNullOrEmpty(orignalDir))
            {
                fileName = orignalDir + Path.DirectorySeparatorChar + fileName;
            }
            fileName += Path.DirectorySeparatorChar;
            ZipEntry z = new ZipEntry(fileName);
            zips.PutNextEntry(z);
        }
        /// <summary>
        /// 递归压缩文件夹内所有文件和子文件夹
        /// </summary>
        /// <param name="orignalDir">外层文件夹</param>
        /// <param name="dir">被压缩文件夹</param>
        /// <param name="zips">流</param>
        private void CompressFolder(string orignalDir, string dir, ZipOutputStream zips)
        {
            // 压缩当前文件夹下所有文件
            string[] names = Directory.GetFiles(dir);
            foreach (string fileName in names)
            {
                AddFile(orignalDir, fileName, zips);
            }
            // 压缩子文件夹
            names = Directory.GetDirectories(dir);
            foreach (string folderName in names)
            {
                AddFolder(orignalDir, folderName, zips);

                string _orignalDir = Path.GetFileName(folderName);
                if (!string.IsNullOrEmpty(orignalDir))
                {
                    _orignalDir = orignalDir + Path.DirectorySeparatorChar + _orignalDir;
                }
                CompressFolder(_orignalDir, folderName, zips);
            }
        }
        #endregion private

        #endregion 压缩文件

        #region 解压文件

        /// <inheritdoc/>
        public static string UnZipFile(string zipFilePath, string unZipDir = null, string password = null)
        {
            if (!File.Exists(zipFilePath))
            {
                throw new Exception($"压缩文件【{zipFilePath}】不存在！");
            }
            //解压文件夹为空时默认与压缩文件同一级目录下，跟压缩文件同名的文件夹   
            if (string.IsNullOrWhiteSpace(unZipDir))
            {
                unZipDir = Path.GetDirectoryName(zipFilePath);
                string name = Path.GetFileNameWithoutExtension(zipFilePath);
                unZipDir = Path.Combine(unZipDir, name);
            }

            string unZipDir2 = unZipDir;
            char lastChar = unZipDir[unZipDir.Length - 1];
            if (lastChar != '/' && lastChar != '\\')
            {
                unZipDir += Path.DirectorySeparatorChar;
            }

            if (!Directory.Exists(unZipDir))
                Directory.CreateDirectory(unZipDir);

            //解压
            UnZipProcess(zipFilePath, unZipDir, password);

            return unZipDir2;
        }
        private static void UnZipProcess(string zipFilePath, string unZipDir, string password)
        {
            ZipInputStream zipInput = null;
            FileStream fileStream = null;
            try
            {
                fileStream = File.OpenRead(zipFilePath);
                zipInput = new ZipInputStream(fileStream);
                zipInput.Password = password;
                ZipEntry theEntry;
                while ((theEntry = zipInput.GetNextEntry()) != null)
                {
                    string tempPath = unZipDir + theEntry.Name;
                    if (theEntry.IsDirectory)
                    {
                        if (!Directory.Exists(tempPath))
                        {
                            Directory.CreateDirectory(tempPath);
                        }
                    }
                    else
                    {
                        using (FileStream streamWriter = File.Create(tempPath))
                        {
                            byte[] buffer = new byte[10240];
                            int size = zipInput.Read(buffer, 0, buffer.Length);
                            while (size > 0)
                            {
                                streamWriter.Write(buffer, 0, size);
                                size = zipInput.Read(buffer, 0, buffer.Length);
                            }
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                throw;
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                    fileStream.Dispose();
                }
                if (zipInput != null)
                {
                    zipInput.Close();
                    zipInput.Dispose();
                }
            }
        }

        #endregion
    }
}
